﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace DataService.Extensions
{
    /// <summary>
    /// 解析lamdba
    /// </summary>
    public class ResolveExpress
    {
        public Dictionary<string, object> Argument;
        public StringBuilder StringBuilder = new StringBuilder();
        private string ParameterPrefix;
        private int paraNum = 0;
        private List<string> paraList = new List<string>();
        public Dictionary<string, object> ResolveExpression<T>(Expression expression,string parameterPrefix)
        {
            try
            {
                ParameterPrefix = parameterPrefix;
                if (string.IsNullOrWhiteSpace(ParameterPrefix))
                {
                    ParameterPrefix = "@";
                }
                this.Argument = new Dictionary<string, object>();
                string sql = Resolve<T>(expression);
                Dictionary<string, object> dic = new Dictionary<string, object>();
                dic["sql"] = sql;
                dic["values"] = Argument;
                return dic;
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("服务业务发生错误"));
            }
        }
        /// <summary>
        /// string[] tempArray
        /// xxx.字段=tempArray[0] 获取不到（以后解决）
        /// Delete<PB_File>(x => List.Contains(x.FileID)) 获取不到以后解决
        /// </summary>
        public virtual string Resolve<T>(Expression expression, Expression bodyLeft = null)
        {
            if (expression is LambdaExpression)
            {
                LambdaExpression lambda = expression as LambdaExpression;
                expression = lambda.Body;
                return Resolve<T>(expression);
            }
            if (expression is BinaryExpression)
            {
                BinaryExpression binary = expression as BinaryExpression;
                if (binary.Left is MemberExpression & binary.Right is ConstantExpression)//解析x=>x.Name=="123" x.Age==123这类
                    return ResolveFunc(binary.Left, binary.Right, binary.NodeType);
                if (binary.Left is MethodCallExpression & binary.Right is ConstantExpression)//解析x=>x.Name.Contains("xxx")==false这类的
                {
                    object value = (binary.Right as ConstantExpression).Value;
                    return ResolveLinqToObject<T>(binary.Left, value, binary.NodeType);
                }
                if (binary.Left is MemberExpression & binary.Right is MemberExpression)//解析x=>x.Date==DateTime.Now这种
                {
                    //优化时，可以通过前缀判断 等号右边是否是值；等号右边两种情况   如 x.width>x.height ；decimal 变量名=5, x.value=变量名
                    try
                    {
                        LambdaExpression lambda = Expression.Lambda(binary.Right);
                        Delegate fn = lambda.Compile();
                        ConstantExpression value = Expression.Constant(fn.DynamicInvoke(null), binary.Right.Type);
                        return ResolveFunc(binary.Left, value, binary.NodeType);
                    }
                    catch (Exception)
                    {
                        string leftName = (binary.Left as MemberExpression).Member.Name;
                        string rightName = (binary.Right as MemberExpression).Member.Name;

                        var tempOperator = GetOperator(expression.NodeType);

                        string Result = string.Format("({0} {1} {2})", leftName, tempOperator, rightName);
                        return Result;
                    }
                }
            }
            if (expression is UnaryExpression)
            {
                UnaryExpression unary = expression as UnaryExpression;
                if (unary.Operand is MethodCallExpression)//解析!x=>x.Name.Contains("xxx")或!array.Contains(x.Name)这类
                    return ResolveLinqToObject<T>(unary.Operand, false);
                if (unary.Operand is MemberExpression & unary.NodeType == ExpressionType.Not)//解析x=>!x.isDeletion这样的 
                {
                    ConstantExpression constant = Expression.Constant(false);
                    return ResolveFunc(unary.Operand, constant, ExpressionType.Equal);
                }
                if (unary.Operand.Type.ToString().Contains("System.Guid"))
                {
                    return ResolveFunc(bodyLeft, expression, ExpressionType.Equal);
                }
            }
            if (expression is MemberExpression & expression.NodeType == ExpressionType.MemberAccess)//解析x=>x.isDeletion这样的 
            {
                MemberExpression member = expression as MemberExpression;
                if (member.Type.ToString().Contains("System.Boolean"))
                {
                    ConstantExpression constant = Expression.Constant(true);
                    return ResolveFunc(member, constant, ExpressionType.Equal);
                }
                //PropertyInfo propertyInfo = member.Member as PropertyInfo;
                //string sss = propertyInfo.PropertyType.ToString();
            }
            if (expression is MethodCallExpression)//x=>x.Name.Contains("xxx")或array.Contains(x.Name)这类
            {
                MethodCallExpression methodcall = expression as MethodCallExpression;
                return ResolveLinqToObject<T>(methodcall, true);
            }
            if (expression is ConstantExpression && expression.NodeType == ExpressionType.Constant)
            {
                var Value = (expression as ConstantExpression).Value;
                if (Value.ToString().ToUpper() == "TRUE")
                {
                    return "(1=1)";
                }
            }
            var body = expression as BinaryExpression;
            if (body == null)
            {
                // throw new Exception("无法解析" + expression);

                return "";
            }
            else
            {
                var Operator = GetOperator(body.NodeType);
                var Left = Resolve<T>(body.Left);
                var Right = Resolve<T>(body.Right, body.Left);
                if (string.IsNullOrEmpty(Right))
                {
                    return Left;
                }
                if (string.IsNullOrEmpty(Left))
                {
                    return string.Format("({0})", Right);
                }
                string Result = string.Format("({0} {1} {2})", Left, Operator, Right);
                return Result;
            }
        }
        private void ResolveFuncValue(Expression bodyLeft, Expression bodyRight)
        {
            if (bodyRight.Type.ToString().Contains("System.Guid"))
            {
                var Name = (bodyLeft as MemberExpression).Member.Name;
                var Value = GetValueWithExpressionsAndReflection(bodyRight);
                SetArgument(Name, Value.ToString());
            }
            else
            {
                var Value = (bodyRight as ConstantExpression).Value;
                var Name = (bodyLeft as MemberExpression).Member.Name;
                SetArgument(Name, Value.ToString());
            }
        }
        public virtual Guid GetValueWithExpressionsAndReflection(Expression right)
        {
            if (right is ConstantExpression)
            {
                return Guid.Parse(right.ToString());
            }
            MemberExpression memberExpression = (MemberExpression)((UnaryExpression)right).Operand;
            // ConstantExpression constantExpression = (ConstantExpression)memberExpression.Expression;
            ConstantExpression constantExpression = memberExpression.Expression as ConstantExpression;

            FieldInfo member = (FieldInfo)memberExpression.Member;
            var instance = constantExpression.Value;
            var guid = member.GetValue(instance);
            return (Guid)guid;
        }
        public virtual Guid GetValueWithExpressionsAndReflection<T>(Expression<Func<T, bool>> selector)
        {
            BinaryExpression binaryExpression = (BinaryExpression)selector.Body;
            MemberExpression memberExpression = (MemberExpression)((UnaryExpression)binaryExpression.Right).Operand;
            ConstantExpression constantExpression = (ConstantExpression)memberExpression.Expression;

            FieldInfo member = (FieldInfo)memberExpression.Member;
            var instance = constantExpression.Value;
            var guid = member.GetValue(instance);
            return (Guid)guid;
        }
        public virtual string ResolveFunc(Expression left, Expression right, ExpressionType expressiontype)
        {
            if (!left.Type.ToString().Contains("System.Boolean") && right.Type.ToString().Contains("System.Boolean"))
            {
                string Name = (left as MemberExpression).Member.Name;
                var Operator = GetOperator(expressiontype);
                string CompName = SetParaList(Name);
                string Result = string.Format("({0} {1} {2})", Name, Operator, CompName);
                return Result;
            }
            else if (left.Type.ToString().Contains("System.Boolean") && right is ConstantExpression && right.NodeType == ExpressionType.Constant)
            {
                var Value = (right as ConstantExpression).Value;
                string Name = (left as MemberExpression).Member.Name;
                var Operator = GetOperator(expressiontype);
                int booValue = Convert.ToBoolean(Value) ? 1 : 0;
                string CompName = SetArgument(Name, booValue);
                string Result = string.Format("({0} {1} {2})", Name, Operator, CompName);
                return Result;
            }
            else if (left.Type.ToString().Equals("System.Guid"))
            {
                string Name = (left as MemberExpression).Member.Name;

                var Value = GetValueWithExpressionsAndReflection(right);
                string CompName = SetArgument(Name, Value.ToString());
                var Operator = GetOperator(expressiontype);
                string Result = string.Format("({0} {1} {2})", Name, Operator, CompName);
                return Result;
            }
            else if (left.Type.ToString().Equals("System.Nullable`1[System.Guid]"))
            {
                string Name = (left as MemberExpression).Member.Name;

                var Value = GetValueWithExpressionsAndReflection(right);
                string CompName = SetArgument(Name, Value.ToString());
                var Operator = GetOperator(expressiontype);
                string Result = string.Format("({0} {1} {2})", Name, Operator, CompName);
                return Result;
            }
            else
            {
                var Value = (right as ConstantExpression).Value;
                string Name = (left as MemberExpression).Member.Name;
                var Operator = GetOperator(expressiontype);
                string valueFormate = Value == null ? string.Empty : Value.ToString();
                string CompName = SetArgument(Name, valueFormate);
                string Result = string.Format("({0} {1} {2})", Name, Operator, CompName);
                return Result;
            }

        }


        /// <summary>
        /// 根据条件生成对应的sql查询操作符
        /// </summary>
        /// <param name="expressiontype"></param>
        /// <returns></returns>
        private string GetOperator(ExpressionType expressiontype)
        {
            switch (expressiontype)
            {
                case ExpressionType.And:
                    return "and";
                case ExpressionType.AndAlso:
                    return "and";
                case ExpressionType.Or:
                    return "or";
                case ExpressionType.OrElse:
                    return "or";
                case ExpressionType.Equal:
                    return "=";
                case ExpressionType.NotEqual:
                    return "<>";
                case ExpressionType.LessThan:
                    return "<";
                case ExpressionType.LessThanOrEqual:
                    return "<=";
                case ExpressionType.GreaterThan:
                    return ">";
                case ExpressionType.GreaterThanOrEqual:
                    return ">=";
                default:
                    throw new Exception(string.Format("不支持{0}此种运算符查找！" + expressiontype));
            }
        }




        public virtual string ResolveLinqToObject<T>(Expression expression, object value, ExpressionType? expressiontype = null)
        {
            try
            {
                var MethodCall = expression as MethodCallExpression;
                var MethodName = MethodCall.Method.Name;
                switch (MethodName)//这里其实还可以改成反射调用，不用写switch
                {
                    case "Contains":
                        if (MethodCall.Object == null)
                            return In(MethodCall, value);
                        if (MethodCall.Object != null && MethodCall.Object.Type.Name == "List`1")
                            throw new Exception(string.Format("不支持List.Contains，请使用string[].Contains", MethodName));
                        return Like(MethodCall);
                    case "Count":
                        return Len(MethodCall, value, expressiontype.Value);
                    case "LongCount":
                        return Len(MethodCall, value, expressiontype.Value);
                    case "Equals":
                        return Equals<T>(expression);
                    case "EndsWith":
                    case "StartsWith":
                        return Like(MethodCall);

                    default:
                        throw new Exception(string.Format("不支持{0}方法的查找！", MethodName));
                }
            }
            catch (Exception ex)
            {
                throw new Exception("不支持方法的查找！");
            }

        }
        private string SetParaList(string paraName)
        {
            int count = paraList.Count(x => x.Equals(paraName));
            if (count > 0)
            {
                paraNum++;
                paraName = paraName + paraNum;
            }
            paraList.Add(paraName);
            return ParameterPrefix + paraName;
        }

        public virtual string SetArgument(string name, object value)
        {
            string tempKey = name;


            while (Argument.ContainsKey(tempKey))
            {
                int code = Guid.NewGuid().GetHashCode();
                if (code == 0)
                    code *= -1;
                code = System.Math.Abs(code);
                tempKey = tempKey + code;
            }
            Argument[tempKey] = value;
            return ParameterPrefix + tempKey;
        }

        private string In(MethodCallExpression expression, object isTrue)
        {
            var Argument1 = (expression.Arguments[0] as MemberExpression).Expression as ConstantExpression;
            // var Argument2 = expression.Arguments[1] as MemberExpression;
            string fieldName = (expression.Arguments[1] as MemberExpression).Member.Name;
            string runTimeFieldName = (expression.Arguments[0] as MemberExpression).Member.Name;
            // var Value = (right as ConstantExpression).Value;
            //if (expression.Arguments[1].Type.ToString().Contains("System.Guid"))
            //{
            //    fieldName = (expression.Arguments[1] as MemberExpression).Member.Name;
            //}
            //else
            //{
            //    fieldName = (((expression.Arguments[1] as UnaryExpression).Operand) as MemberExpression).Member.Name;
            //}
            //原来的获取string[]方式
            // var Field_Array = Argument1.Value.GetType().GetFields().First();
            //修改后获取string[]方式
            var Field_Array = Argument1.Value.GetType().GetRuntimeField(runTimeFieldName);
            List<string> SetInPara = new List<string>();

            object objValue = Field_Array.GetValue(Argument1.Value);
            if (objValue.GetType().ToString().Equals("System.Guid[]"))
            {
                Guid[] Array = objValue as Guid[];
                for (int i = 0; i < Array.Length; i++)
                {
                    string Name_para = fieldName + i;
                    string Value = Array[i].ToString();
                    string Key = SetArgument(Name_para, Value);
                    SetInPara.Add(Key);
                }
            }
            else if (objValue.GetType().ToString().Equals("System.Nullable`1[System.Guid][]"))
            {
                Guid?[] Array = objValue as Guid?[];
                for (int i = 0; i < Array.Length; i++)
                {
                    string Name_para = fieldName + i;
                    string Value = Array[i].ToString();
                    string Key = SetArgument(Name_para, Value);
                    SetInPara.Add(Key);
                }
            }
            else
            {
                int tempIndex = 0;
                var multiExec = objValue as IEnumerable;
                foreach (var item in multiExec)
                {
                    tempIndex++;
                    string Name_para = fieldName + tempIndex;
                    string Key = SetArgument(Name_para, item);
                    SetInPara.Add(Key);
                }
            }
            //取出实体的字段名
            string Operator = Convert.ToBoolean(isTrue) ? "in" : " not in";
            string CompName = string.Join(",", SetInPara);
            string Result = string.Format("{0} {1} ({2})", fieldName, Operator, CompName);
            //这里也需要判断是否是字符串
            return Result;
        }
        delegate object MemberGetDelegate(object obj);
        private string Equals<T>(Expression expression)
        {
            object objValue = GetMethodCallValue(expression);
            if ((expression as MethodCallExpression).Arguments[0] is ConstantExpression)
            {
                string Name = ((expression as MethodCallExpression).Object as MemberExpression).Member.Name;
                string CompName = SetArgument(Name, objValue);
                string Result = string.Format("{0} ={1}", Name, CompName);
                return Result;
            }
            else
            {
                string paraName = ((expression as MethodCallExpression).Object as MemberExpression).Member.Name;
                string CompName = SetArgument(paraName, objValue);
                string Result = string.Format("{0} ={1}", paraName, CompName);
                return Result;

            }
        }

        public object GetMethodCallValue(Expression expression)
        {
            if ((expression as MethodCallExpression).Arguments[0] is ConstantExpression)
            {
                object value = (((expression as MethodCallExpression).Arguments[0]) as ConstantExpression);
                return value;
            }
            else
            {
                object objValue = null;
                var tempMemberExpress = (expression as MethodCallExpression).Arguments[0] as MemberExpression;
                var tempExpression = tempMemberExpress.Expression;
                if (tempExpression is ConstantExpression)
                {
                    objValue = (tempExpression as ConstantExpression).Value;
                    if (tempMemberExpress.Member.MemberType == MemberTypes.Field)
                    {
                        objValue = ((FieldInfo)tempMemberExpress.Member).GetValue(objValue);
                    }
                    if (tempMemberExpress.Member.MemberType == MemberTypes.Property)
                    {
                        objValue = ((PropertyInfo)tempMemberExpress.Member).GetValue(objValue);
                    }
                }
                else
                {
                    string valueParaName = ((expression as MethodCallExpression).Arguments[0] as MemberExpression).Member.Name;
                    object valueObjd = Expression.Lambda(((expression as MethodCallExpression).Arguments[0] as MemberExpression).Expression).Compile().DynamicInvoke();
                    Type type = valueObjd.GetType();
                    PropertyInfo property = type.GetProperty(valueParaName);
                    objValue = property.GetValue(valueObjd, null);
                }
                #region 动态编译委托性能目前最好,但是总报错,以后再研究
                //// PropertyInfo property = valueObjd.GetType().GetRuntimeProperty(Name);
                //MemberGetDelegate memberGet = (MemberGetDelegate)System.Delegate.CreateDelegate(typeof(MemberGetDelegate), property.GetGetMethod());
                //object value = memberGet(valueObjd);
                #endregion
                return objValue;
            }
        }
        public virtual string Like(MethodCallExpression expression)
        {
            string Result = string.Empty;
            if (expression.Arguments[0] is ConstantExpression)
            {
                //数据库字段.Contains("字符串值")
                object Temp_Vale = (expression.Arguments[0] as ConstantExpression).Value;
                string Value = string.Format(GetLikeNode(expression), Temp_Vale);
                string Name = (expression.Object as MemberExpression).Member.Name;
                string CompName = SetArgument(Name, Value);
                Result = string.Format("{0} like {1}", Name, CompName);
            }
            if (expression.Arguments[0] is MemberExpression)
            {
                //数据库字段.Contains("字符串值")
                string Name = (expression.Object as MemberExpression).Member.Name;
                object Temp_Vale = GetMethodCallValue(expression);
                string Value = string.Format(GetLikeNode(expression), Temp_Vale);
                string CompName = SetArgument(Name, Value);
                Result = string.Format("{0} like {1}", Name, CompName);
            }
            return Result;
        }
        public string GetLikeNode(MethodCallExpression m)
        {
            if (m == null) throw new ArgumentNullException("MethodCallExpression");

            string format;
            switch (m.Method.Name)
            {
                case "StartsWith":
                    format = "{0}%";
                    break;
                case "Contains":
                    format = "%{0}%";
                    break;
                case "EndsWith":
                    format = "%{0}";
                    break;
                default:
                    throw new NotSupportedException(m.NodeType + " is not supported!");
            }
            return format;
        }
        private string Len(MethodCallExpression expression, object value, ExpressionType expressiontype)
        {
            object Name = (expression.Arguments[0] as MemberExpression).Member.Name;
            string Operator = GetOperator(expressiontype);
            string CompName = SetArgument(Name.ToString(), value.ToString());
            string Result = string.Format("len({0}){1}{2}", Name, Operator, CompName);
            return Result;
        }

    }
}
